ls-tree command
The ls-tree command is used to list the contents of a given tree object showing their metadata such as mode, type, hash, and name. This command is useful for inspecting a specific version of a repository’s directory structure.
Command Implementation
- Parse the Command Arguments:
In Git, ls-tree function can take alot of flags but for simplicity in our implementation we'll create only one flag --name-only. This flag is used when we want to list only filenames (instead of the "long" output), one per line.
./index.php
$hash = $args[0] ?? null;
$flag = false;
if (isset($args[0]) && $args[0] === "--name-only") {
if (count($args) < 2) {
echo "Error: Hash required.\n";
return;
}
$flag = true;
$hash = $args[1];
}
- Extract the first argument which is the tree object's hash.
- Checks if the --name-only flag is passed:
- If --name-only is found, it requires an additional argument (the hash).
- If no hash is provided after the flag, it prints an error and exits.
- Otherwise, it sets $flag = true, meaning the output will include only filenames.
- Locate and retrieve the tree object:
./index.php
$dir = '.mygit/objects/' . substr($hash, 0, 2);
$file = $dir . '/' . substr($hash, 2);
if (!file_exists($file)) {
echo "Error: Tree object not found.\n";
return;
}
$compressed = file_get_contents($file);
$data = gzuncompress($compressed);
- We create our directory path
$dirby concatenating'.mygit/objects/'with the first two characters in$hash. - We create our file path
$fileby concatenating$dirpath with/and the rest of the file hash. - If the file doesn't exist output an error and return.
- Then we get the compressed file contents from
$fileand store the uncompressed data in$data.
- Validate tree object format:
./index.php
$parts = explode("\0", $data, 2);
if (count($parts) < 2) {
echo "Error: Invalid object format.\n";
return;
}
[$header, $content] = $parts;
if (!str_starts_with($header, 'tree ')) {
echo "Error: Not a tree object.\n";
return;
}
- Split the uncompressed data into two parts header and content.
- Validate that
$partshas the two parts and output an error and return if not. - Check if our header starts with
'tree '(which means it is a tree object) and output an error and return if not.
- Parse and display tree entries:
./index.php
$lines = explode("\n", trim($content));
foreach ($lines as $line) {
if (empty($line))
continue;
$parts = explode(" ", trim($line), 4);
if (count($parts) !== 4) {
continue;
}
[$mode, $type, $hash, $name] = $parts;
if ($flag) {
echo $name . "\n";
} else {
echo "$mode $type $hash $name\n";
}
}
- Split the tree content into lines, each representing a file entry.
- Iterates through each line:
- Skips empty lines.
- Splits the line into four parts: mode, type, hash, and name.
- If --name-only is set, only prints filenames.
- Otherwise, print the full metadata: mode, type, hash, name.
Full code for this section:
./index.php
function command_ls_tree($args)
{
$hash = $args[0] ?? null;
$flag = false;
if (isset($args[0]) && $args[0] === "--name-only") {
if (count($args) < 2) {
echo "Error: Hash required.\n";
return;
}
$flag = true;
$hash = $args[1];
}
$dir = '.mygit/objects/' . substr($hash, 0, 2);
$file = $dir . '/' . substr($hash, 2);
if (!file_exists($file)) {
echo "Error: Tree object not found.\n";
return;
}
$compressed = file_get_contents($file);
$data = gzuncompress($compressed);
$parts = explode("\0", $data, 2);
if (count($parts) < 2) {
echo "Error: Invalid object format.\n";
return;
}
[$header, $content] = $parts;
if (!str_starts_with($header, 'tree ')) {
echo "Error: Not a tree object.\n";
return;
}
$lines = explode("\n", trim($content));
foreach ($lines as $line) {
if (empty($line))
continue;
$parts = explode(" ", trim($line), 4);
if (count($parts) !== 4) {
continue;
}
[$mode, $type, $hash, $name] = $parts;
if ($flag) {
echo $name . "\n";
} else {
echo "$mode $type $hash $name\n";
}
}
}